#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <gmp.h>
#include <pthread.h>


/* VARIAVEIS GLOBAIS */
 
mpf_t a0;
mpf_t a1;
mpf_t b0;
mpf_t b1;
mpf_t t0;
mpf_t t1;
mpf_t p0;
mpf_t p1;
mpf_t PI;
mpf_t tmp;

pthread_mutex_t mutex;

pthread_t threadA;
pthread_t threadB;
pthread_t threadP;
pthread_t threadPI;

FILE *gauss_paralelo;


/* ----- THREADS -----*/

// a1 = (a0+b0)/2 
void *calculaA(){
	pthread_mutex_lock(&mutex);
	mpf_add(a1,a0,b0);
	mpf_div_ui(a1,a1,2);
	// t1 = t0 - p0*[(a0-a1)ˆ2]	
	mpf_sub(tmp,a0,a1);
	mpf_pow_ui(tmp,tmp,2);
	mpf_mul(t1, p0, tmp);
	mpf_sub(t1, t0,t1);
	
	pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}


//b1 = raiz(a0*b0)
void *calculaB(){
	pthread_mutex_lock(&mutex);
	mpf_mul(b1, a0,b0);
	mpf_sqrt(b1,b1);
	pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}

//p1 = 2*p0
void *calculaP(){
	pthread_mutex_lock(&mutex);
	
	mpf_mul_ui(p1,p0,2);
	
	pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}

// PI = (a0+b0)ˆ2 / 4*t0
void *calculaPI(){
	pthread_mutex_lock(&mutex);
	
	mpf_add(tmp, a0, b0);
	mpf_pow_ui(tmp, tmp, 2);
	mpf_mul_ui(PI, t0, 4);
	mpf_div(PI,tmp,PI);
	
	mpf_out_str(gauss_paralelo,10,0,PI);
	
	pthread_mutex_unlock(&mutex);
    pthread_exit(NULL);
}


int main(void)
{
	struct timeval start, end;
	int time;
	gauss_paralelo = fopen("gauss_paralelo.txt", "w+");
	// precisao
    mpf_set_default_prec (33219281);
	
	int i = 0;
    pthread_mutex_init(&mutex, NULL);
	
	//inicializao
	mpf_init_set_ui(a0, 1);
	mpf_init(b0);
	mpf_init(t0);
	mpf_init_set_ui(p0, 1);
	mpf_init(a1);
	mpf_init(b1);
	mpf_init(t1);
	mpf_init(p1);
	mpf_init(PI);
	mpf_init(tmp);
	
	gettimeofday(&start, NULL);
	printf("Calculando pi...\n");
	
	/* ------ VALORES INICIAIS ------ */
	
	//Raiz(2)
	mpf_sqrt_ui(tmp,2);
	// b0 = 1/raiz(2)
	mpf_ui_div(b0, 1, tmp);
	// t0 = 1/4
	mpf_div_ui(t0,a0,4);
	
	
	for(i=0;i<25;i++){
		// criacao das threads
		pthread_create(&threadA, NULL, calculaA, NULL);
		pthread_create(&threadB, NULL, calculaB, NULL);
		pthread_create(&threadP, NULL, calculaP, NULL);
		pthread_create(&threadPI, NULL, calculaPI, NULL);
		
		
		pthread_join(threadA, NULL);
		pthread_join(threadB, NULL);
		pthread_join(threadP, NULL);
		pthread_join(threadPI, NULL);
		
		// guardando novos valores
		mpf_set(a0, a1);
		mpf_set(b0, b1);
		mpf_set(t0, t1);
		mpf_set(p0, p1);		
		
		
	}
	
	printf("Pronto\n");

	gettimeofday(&end, NULL);
	time = start.tv_sec - end.tv_sec;

	printf("\nNumero de iteracoes: %d\nTempo para calculo: %d segundos\n", i, time);

	//libera memoria
	mpf_clear(a0);
	mpf_clear(a1);
	mpf_clear(b0);
	mpf_clear(b1);
	mpf_clear(t0);
	mpf_clear(t1);
	mpf_clear(p0);
	mpf_clear(p1);
	mpf_clear(PI);
	mpf_clear(tmp);
	
	return EXIT_SUCCESS;
}


